<?php
namespace app\api\controller;

/**
 * Author: aventury(aventuro@sina.com)
 * Desc: 参数统一处理方法
 * 1. 防止接口被大规模调用消耗系统资源: 添加时间戳time
 * 2. 防止接口数据被黑客篡改(伪造请求)：增加校验参数mac
 * 3. 过滤time与mac, 拿到真正的请求参数
 * note: 上线或者部署到环境上才进行time与mac的校验
 * Data: 2019年8月9日
 */

use think\Request;
use think\Controller;
use think\Validate;
use think\Db;
use think\Image;

class Common extends Controller {
    protected $request; // 用来处理参数
    protected $validater; // 用来校验数据 / 参数
    protected $params; // 过滤后符合要求的参数
    protected $__HTTP__; // http的协议
    protected $__DATAURL__; // 所有图片位置
    protected $rules = array(
        'Index' => array(
            'index' => array()
        ),
        'User' => array(
            'login' => array(
                'user_name' => 'require',
                'user_pwd'  => 'require|length:32',
            ),
            'register' => array(
                'user_name' => 'require',
                'user_pwd'  => 'require|length:32',
                'code'      => 'require|number|length:6'
            ),
            'upload_head_img' => array(
                'user_id'     => 'require',
                'user_icon'   => 'require|image|fileSize:2000000000|fileExt:jpg,png,bmp,jpeg'
            ),
            'change_pwd'       => array(
                'user_name'    => 'require',
                'user_ini_pwd' => 'require|length:32',
                'user_pwd'     => 'require|length:32',
            ),
            'find_pwd'      => array(
                'user_name' => 'require',
                'user_pwd'  => 'require|length:32',
                'code'      => 'require|number|length:6',
            ),
            'bind_phone'    => array(
                'user_id'   => 'require|number',
                'phone'     => ['require','regex'=>'/^1((3[\d])|(4[5,6,7,9])|(5[0-3,5-9])|(6[5-7])|(7[0-8])|(8[\d])|(9[1,8,9]))\d{8}$/'],
                'code'      => 'require|number|length:6',
            ),
            'bind_email'    => array(
                'user_id'   => 'require|number',
                'email'     => 'require|email',
                'code'      => 'require|number|length:6',
            ),
            'bind_username'        => array(
                'user_id' => 'require|number',
                'user_name'  => 'require',
                'code'      => 'require|number|length:6',
            ),
            'set_nickname'        => array(
                'user_id' => 'require|number',
                'user_nickname'  => 'require|chsDash',
            )
        ),
        'MsgCode' => array(
            'get_code' => array(
                'username' => 'require',
                'is_exist' => 'require|number|length:1',
            ),
        ),
        'Article' => array(
            'add_article' => array(
                'article_uid'   => 'require|number',
                'article_title' => 'require|chsDash',
            ),
            'article_list' => array(
                'user_id'  => 'require|number',
                'num'      => 'number',
                'page'     => 'number',
            ),
            'article_detail' => array(
                'article_id' => 'require|number',
            ),
            'update_article' => array(
                'article_id' => 'require|number',
                'article_title'=>'require'
            ),
            'del_article' => array(
                'article_id' => 'require|number',
            ),
        ),
    );

    protected function _initialize() {
        parent::_initialize();
        $this->request = Request::instance();
        // ---------------------上线时，或者客户端进行了相应的参数传值，才取消注释--------------------------------------
        // $this->checked_time($this->request->only(['time']));
        // $this->check_mac($this->request->param(), $this->request->method());
        // $this->params = $this->check_params($this->request->param(true));

        //php 判断http还是https
    	$http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://'; 
    	//所有图片路径
	    $this->__DATAURL__ = $http_type.$_SERVER['SERVER_NAME'].'/bookShop/public/data/';
        $this->__HTTP__ = $http_type;
    }

    /**
     * 验证请求是否超时
     * @param [array] $arr [包含时间戳的数组]
     * @return [json]      [检测结果]
     */
    public function checked_time($arr) {
        if(!isset($arr['time']) || intval($arr['time']) <=1 ) {
            $this->return_msg(0, '时间戳不正确！');
        }
        if((time() - intval($arr['time'])) > 60) {
            $this->return_msg(0, '请求超时！');
        }
    }

    /**
     * api 数据返回
     * @param [int] $status [结果码 1：正常 / 0：数据问题 ]
     * @param [string] $msg [接口要返回的提示信息]
     * @param [array] $data [接口要返回的数据]
     * @return [string]      [最终的json数据]
     */
    public function return_msg($status, $msg, $data=[]) {
        /****  组合数组  *****/
        $return_data['status'] = $status;
        $return_data['msg'] = $msg;
        $return_data['data'] = $data;

        /****   返回信息并终止脚本 *****/
        echo json_encode($return_data);
        die;
    }

    /**
     * 验证mac(防止篡改数据)
     * @param [array] $arr [全部请求参数]
     * @param [string] $methods [请求类型]
     * @return [json]      [mac校验结果]
     */
    public function check_mac($arr, $methods) {
        /****  api传过来的mac  ****/
        if(!isset($arr['mac']) || empty($arr['mac'])) {
            $this->return_msg(0, 'mac不能为空！');
        }
        $app_mac = $arr['mac'];
        /*****  服务器端生成mac  *****/
        unset($arr['mac']);
        unset($arr['time']);
        // 后期如果有登录校验的话，也可以考虑加入token
        // unset($arr['token']);
        $server_mac = '';

        $count = 0;
		$str = '';
		foreach($arr as $k => $v){
			$str .= substr((string)$k, 0, 1);
            if (is_string($v)) {
                $str .= (string)$v;
            } else {
                $str .= implode(",", $v);
            }
            $count++;
		}
		$str .= $count;
		if ($str !== '') {
			if(strtolower($methods) != 'post'){
				$str = urlencode($str);
			}
			$str = md5($str);
			$count = $count % 10;
			$server_mac = substr($str, 0, $count);
			$server_mac .= (string)$count;
			$server_mac .= substr($str, $count);
            $server_mac = strtoupper($server_mac);

            if($app_mac !== $server_mac){
                $this->return_msg(0, 'mac校验错误！');
            }
        }
    }

    /**
    * 验证参数 参数过滤
    * @param  [array] $arr [只校验的需要检验的参数，像time和token不需要检验，在前面$rules有标注那些才是需要检测的参数]
    * @return [return]      [合格的参数数组]
    */
    public function check_params($arr){
        /***** 获取参数的验证规则  *****/
        $rule = $this->rules[$this->request->controller()][$this->request->action()];

         /*********** 验证参数并返回错误  ***********/
        $this->validater = new Validate($rule);
        if (!$this->validater->check($arr)) {
            $this->return_msg(0, $this->validater->getError());
        }

        /*********** 如果正常,通过验证  ***********/
        return $arr;
    }

    /**
     * 检测用户名并返回用户名类别
     * @param [string] $username [用户名，可能是邮箱，也可能是手机号]
     * @return [string]          [检测结果]
     */
    public function check_username($username){
        // 判断是否为邮箱
        $is_email = Validate::is($username, 'email') ? 1 : 0;
        // 判断是否为手机，phone与email组合时很好区分谁是谁
        $is_phone = preg_match('/^1((3[\d])|(4[5,6,7,9])|(5[0-3,5-9])|(6[5-7])|(7[0-8])|(8[\d])|(9[1,8,9]))\d{8}$/', $username) ? 4 : 2; 
        $flag = $is_email + $is_phone;
        switch ($flag) {
            /****  not phone not email  ****/
            case 2:
                $this->return_msg(0, '邮箱或手机号不正确！');
                break;
            /****  is email not phone  ****/
            case 3:
                return 'email';
                break;
            /****  is phone not email  ****/
            case 4:
                return 'phone';
                break;
        }
    }

    /**
     * 检测手机号码/邮箱是否存在于数据库中，总的8种情况，有4种是需要提示的。
     * @param [string] $value [手机号码/邮箱号码]
     * @param [string] $type  [类型]
     * @param [int] $exist    [是否存在 1：是 0：否]
     * @return [string]       [返回json]
     */
    public function check_exist($value, $type, $exist) {
        $type_num  = ($type == "phone") ? 2 : 4;
        $flag      = $type_num + $exist;
        $phone_res = db('user')->where('user_phone', $value)->find();
        $email_res = db('user')->where('user_email', $value)->find();
        switch ($flag) {
        /*********** 2+0 phone need no exist  ***********/
        case 2:
            if ($phone_res) {
                $this->return_msg(0, '此手机号已被占用!');
            }
            break;
        /*********** 2+1 phone need exist  ***********/
        case 3:
            if (!$phone_res) {
                $this->return_msg(0, '此手机号不存在!');
            }
            break;
        /*********** 4+0 email need no exist  ***********/
        case 4:
            if ($email_res) {
                $this->return_msg(0, '此邮箱已被占用!');
            }
            break;
        /*********** 4+1 email need  exist  ***********/
        case 5:
            if (!$email_res) {
                $this->return_msg(0, '此邮箱不存在!');
            }
            break;
        }
    }

    /**
     * 检查验证码
     * @param [string] $user_name   [手机号或者邮箱号]
     * @param [int]    $code        [验证码]
     * @return [type]               [返回结果]
     */
    public function check_code($user_name, $code) {
        // 检测是否超时
        $last_time = session(str_replace('.', '', $user_name) . '_last_send_time');
        if (time() - $last_time > 120) {
            $this->return_msg(0, '验证超时,请在两分钟内验证!');
        }

        // 检测验证码是否正确
        $md5_code = md5($user_name . '_' . md5($code));
        if (session(str_replace('.', '', $user_name) . '_code') !== $md5_code) {
            $this->return_msg(0, '验证码不正确!');
        }

        // 不管正确与否,每个验证码只验证一次
        session(str_replace('.', '', $user_name) . '_code', null);
        session(str_replace('.', '', $user_name) . '_last_send_time', null);
    }

    /**
     * 上传文件到根目录下的uploads文件里
     * @param [file]   $file  [上传的图片文件]
     * @param [string] $type  
     * @return [json]         [返回结果]
     */
    public function upload_file($file, $type = '') {
        // 移动文件到根目录
        $info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');
        if ($info) {
            $path = '/uploads/' . $info->getSaveName();
            // 裁剪图片
            if (!empty($type)) {
                $this->image_edit($path, $type);
            }
            return str_replace('\\', '/', $path);
        } else {
            $this->return_msg(0, $file->getError());
        }
    }

    /**
     * 裁剪图片200*200
     * @param [string]   $path  [上传图片的路径]
     * @param [string] $type  
     * @return [json]         [返回结果]
     */
    public function image_edit($path, $type) {
        $image = Image::open(ROOT_PATH . 'public' . $path);
        switch ($type) {
        case 'head_img':
            $image->thumb(200, 200, Image::THUMB_CENTER)->save(ROOT_PATH . 'public' . $path);
            break;
        }
    }
}